import { Client, FileInfo } from 'basic-ftp'
import path from 'path'
import fs from 'fs'
import EventEmitter from 'events'
import { log } from 'util'

class FTPService extends EventEmitter {
    static instance = null
    static getInstance() {
        if (!FTPService.instance) {
            FTPService.instance = new FTPService()
        }
        return FTPService.instance
    }

    constructor() {
        super()
        if (FTPService.instance) {
            return FTPService.instance
        }
        this.ftpClients = new Map()
        FTPService.instance = this
    }
    // 获取连接地址标识
    getAddress(host, port = 21) {
        return `${host}:${port}`
    }

    // 连接FTP服务器
    async connect({ host, port = 21, user, password }) {
        if (!host) {
            throw new Error('FTP地址不能为空')
        }
        if (!user || !password) {
            throw new Error('FTP用户名或密码不能为空')
        }
        const address = this.getAddress(host, port)
        const client = new Client()
        client.ftp.verbose = true
         if (this.ftpClients.has(address)) {
            return 
         }
        try {
            await client.access({
                host,
                port,
                user,
                password,
                secure: false,
                secureOptions: undefined
            })
            this.emit('connected', { host, port })
            this.ftpClients.set(address, client)
            return true
        } catch (error) {
            this.emit('error', { type: 'connect', error: error.message })
            throw new Error(`连接FTP服务器失败: ${error.message}`)
        }
    }

    // 获取文件列表
    async list({ host, port, path = '/' }) {
        const client = this.getClient(host, port);
        try {
            let pwd = await client.pwd()
            if (path === '/') {
                await client.cd(pwd)
            } else {
                const fullPath = pwd === '/' ? `/${path}` : `${pwd}/${path}`
                await client.cd(fullPath)
            }
            const list = await client.list()
            const formattedList = list.map(item => ({
                ...item,
                isDirectory: item.type === 2,
            }))
            pwd = await client.pwd()
            this.emit('list-success', { host, port, list: formattedList, pwd })
            return list
        } catch (error) {
            this.emit('error', { type: 'list', error: error.message })
            throw new Error(`获取文件列表失败: ${error.message}`)
        }
    }

    // 上传文件
    async upload({host, port, localFilePath, remoteDirPath}) {
        const client = this.getClient(host, port)
        if (!fs.existsSync(localFilePath)) {
            this.emit('error', {
                type: 'upload',
                error: `本地文件不存在: ${localFilePath}`
            })
        }
        try {
            const fileName = path.basename(localFilePath)
            const fileSize = fs.statSync(localFilePath).size
            // 监听传输进度
            client.trackProgress(info => {
                const percent = Math.round((info.bytes / fileSize) * 100)
                // 发送进度事件
                this.emit('upload-progress', {
                    host,
                    port,
                    fileName,
                    bytes: info.bytes,
                    bytesOverall: fileSize,
                    percent,
                })
            })
            await client.uploadFrom(localFilePath, path.join(remoteDirPath, fileName))
            // 清除进度监听
            client.trackProgress()
            // 发送上传完成事件
            this.emit('upload-success', {
                host,
                port,
                localFilePath,
                remotePath: path.join(remoteDirPath, fileName)
            })
            return true
        } catch (error) {
            this.emit('error', { type: 'upload', error: error.message })
            throw new Error(`上传文件失败: ${error.message}`)
        }
    }

    // 下载文件
    async download({host, port, remoteFilePath, localDirPath}) {
        const client = this.getClient(host, port)
        try {
            const fileInfo = await client.size(remoteFilePath)
            const fileSize = fileInfo
            const fileName = path.basename(remoteFilePath) 
            const localFilePath = path.join(localDirPath, fileName)
            client.trackProgress(info => {
                const percent = Math.round((info.bytes / fileSize) * 100)
                this.emit('download-progress', {
                    host,
                    port,
                    fileName,
                    bytes: info.bytes,
                    bytesOverall: fileSize,
                    percent,
                })
            })     
            await client.downloadTo(localFilePath, remoteFilePath)
            client.trackProgress() 
            this.emit('download-success', {
                host,
                port,
                fileName,
                remoteFilePath,
                localFilePath
            })
            return localFilePath
        } catch (error) {
            this.emit('error', { type: 'download', error: error.message })
            throw new Error(`下载文件失败: ${error.message}`)
        }
    }

    // 删除文件
    async remove({ host, port, path }) {
        const client = this.getClient(host, port)
        try {
            let pwd = await client.pwd()
            const fullPath = pwd === '/' ? `${pwd}${path}` : `${pwd}/${path}`
            await client.remove(fullPath)
            this.emit('remove-success', { host, port, pwd })
            return true
        } catch (error) {
            throw new Error(`删除文件失败: ${error.message}`)
        }
    }

    // 获取当前工作目录
    async pwd(host, port) {
        const client = this.getClient(host, port)
        try {
            const pwd = await client.pwd()
            return pwd
        } catch (error) {
            throw new Error(`获取当前目录失败: ${error.message}`)
        }
    }

    // 切换目录
    async cd(host, port, dir) {
        const client = this.getClient(host, port)
        try {
            await client.cd(dir)
            return true
        } catch (error) {
            throw new Error(`切换目录失败: ${error.message}`)
        }
    }

    // 返回上级目录
    async cdup({ host, port }) {
        const client = this.getClient(host, port)
        try {
            await client.cdup()
            const pwd = await client.pwd()
            this.emit('cdup-success', { host, port, pwd })
            return true
        } catch (error) {
            throw new Error(`返回上级目录失败: ${error.message}`)
        }
    }

    // 关闭连接
    async close({ host, port }) {
        const address = this.getAddress(host, port)
        const client = this.ftpClients.get(address)
        if (!client || client.closed) {
            if (this.ftpClients.has(address)) {  
                this.ftpClients.delete(address);
            }
            return;
        }
        try {
            await client.close()
            this.ftpClients.delete(address)
            console.log("关闭连接");
            this.emit('closed', { host, port })
            return true
        } catch (error) {
            this.ftpClients.delete(address)
            this.emit('error', { type: 'close', error: error.message })
            throw new Error(`关闭连接失败: ${error.message}`)
        }
    }
    // 获取FTP客户端实例
    getClient(host, port) {
        const address = this.getAddress(host, port)
        const client = this.ftpClients.get(address)
        if (!client || client.closed) {
            throw new Error(`FTP连接已关闭: ${address}`)
        }
        return client
    }


}
export default FTPService.getInstance()